/**
 * 
 */
package com.novartis.nims.trialsubmitter.views;

import com.novartis.nims.trialsubmitter.AnonymizerFilterException;
import com.novartis.nims.trialsubmitter.ApplicationConfiguration;
import com.novartis.nims.trialsubmitter.ApplicationController;
import com.novartis.nims.trialsubmitter.ModalProgressMonitor;
import com.novartis.nims.trialsubmitter.OdmSinkException;
import com.novartis.nims.trialsubmitter.TrialReaderException;
import com.novartis.nims.trialsubmitter.TrialSubmitterSwingWorker;
import com.novartis.nims.util.HtmlJPanel;
import com.novartis.nims.util.ResourceAccess;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import org.apache.log4j.Logger;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Toolkit;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;


/**
 * @author snydejo1
 *
 */
public abstract class SwingView {
	
	protected ApplicationController Controller = null;  //  @jve:decl-index=0:
	protected ApplicationConfiguration Configuration = null;

	protected JFrame jFrame = null;  //  @jve:decl-index=0:visual-constraint="10,10"
	private HtmlJPanel usageContentPane = null;
	
	
	public SwingView(ApplicationConfiguration config) {
		this.Configuration = config;
		
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		}
		catch (ClassNotFoundException ignore) {}
		catch (InstantiationException ignore) {}
		catch (IllegalAccessException ignore) {}
		catch (UnsupportedLookAndFeelException ignore) {}
		
		//this.getJFrame().setVisible(true);
	}
	
	public void setController(ApplicationController controller) {
		this.Controller = controller;
	}
	
	public abstract String getInputDirectory();
	public abstract String getOutputDirectory();
	public abstract boolean getDoSort();
	public abstract boolean getDoClean();
	public abstract boolean getDoODM();
	public abstract int getSubmissionNumber();

	/**
	 * This method initializes jFrame
	 * 
	 * @return javax.swing.JFrame
	 */
	public abstract JFrame getJFrame();

	/**
	 * This method initializes aboutContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	protected JPanel getUsageContentPane() {
		if (usageContentPane == null) {
			InputStream is = ResourceAccess.getResourceAsStream(ApplicationConfiguration.class, "usage.html");
			String defaultUsage = "<html>No usage information available.</html>";
			if (is == null) {
				usageContentPane = new HtmlJPanel(defaultUsage);
			} else {
				String usage = getInputStreamText(is);
				try { is.close(); } catch (IOException e) { }
				
				if (usage.isEmpty()) {
					usage = defaultUsage;
				}
				usageContentPane = new HtmlJPanel(usage);
			}
			
			Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
			dim.setSize(dim.width * 2.0 / 3.0, dim.height * 2.0 / 3.0);
			usageContentPane.setPreferredSize(dim);
		}
		
		usageContentPane.scrollToTop();
		return usageContentPane;
	}
	

	/**
	 * Read a text input stream completely, using the latin1 encoding.
	 * @param is the input stream to read.
	 * @return the text of the input stream, or an empty string if an error occurred.
	 */
	private static String getInputStreamText(InputStream is) {
		Charset latin1 = Charset.forName("iso-8859-1");
		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(is, latin1));
			StringWriter sw = new StringWriter();
			int n;
			char[] cbuf = new char[1024];
			while ((n=br.read(cbuf,0,cbuf.length)) != -1) sw.write(cbuf,0,n);
			br.close();
			return sw.toString();
		}
		catch (Exception e) { return ""; }
	}
	
	protected class JFileChooserActionListener implements ActionListener {
		private Component mParent;
		private JTextField mTextField;
		
		protected JFileChooserActionListener(Component parent, JTextField field) {
			mParent = parent;
			mTextField = field;
		}

		public void actionPerformed(ActionEvent e) {
			// Create a file chooser
			final JFileChooser chooser = new JFileChooser();
			chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
			chooser.setCurrentDirectory(new File(mTextField.getText()));
			int returnVal = chooser.showOpenDialog(mParent);
		    if (returnVal == JFileChooser.APPROVE_OPTION) {
		    	mTextField.setText(chooser.getSelectedFile().getAbsolutePath());
		    }
		}
	}
	
	private class TrialSubmitterProgressMonitor extends ModalProgressMonitor implements PropertyChangeListener {
		private TrialSubmitterSwingWorker mWorker;
		private boolean mFinishedWorkerIsProcessed = false;
		
		private TrialSubmitterProgressMonitor(TrialSubmitterSwingWorker worker, Component parentComponent, Object message, String note, int min, int max) {
			super(parentComponent, message, note, min, max);
			mWorker = worker;
			mWorker.addPropertyChangeListener(this);
		}

		public void propertyChange(PropertyChangeEvent evt) {
			if ("progress" == evt.getPropertyName()) {
				if (!mWorker.isDone()) {
					if (this.isCanceled()) {
						// if user has canceled, then cancel the worker
						mWorker.cancel(true);
					} else { //otherwise update the progress monitor
						int progress = (Integer) evt.getNewValue();
						this.setProgress(progress);
					}
				}
				
	        } else if ("message" == evt.getPropertyName()) {
        		String message = (String) evt.getNewValue();
        		this.setNote(message);
        		
	        } else if ("determinate" == evt.getPropertyName()) {
	        	boolean determinate = (Boolean) evt.getNewValue();
	        	this.setIndeterminate(!determinate);
	        	
        	} else if ("state" == evt.getPropertyName()) {
	        	if (mWorker.isDone() && !(mFinishedWorkerIsProcessed)) {
	        		mFinishedWorkerIsProcessed = true;
	        		
	                Toolkit.getDefaultToolkit().beep();
	                
	                try {
	                	mWorker.get();
	                	Logger.getRootLogger().info("Execution finished successfully.");

	            		JOptionPane.showMessageDialog(jFrame,
	            				"Finished successfully!",
	            				"Info",
	            				JOptionPane.INFORMATION_MESSAGE);
	                } catch (CancellationException e) {
						Logger.getRootLogger().info("Execution finished due to user cancelation.");
	                } catch (InterruptedException e) {
	                	// do nothing
	                } catch (ExecutionException e) {
						Throwable cause = e.getCause();
						if (cause == null) cause = e;
						
						String message;
						if (
								(cause instanceof TrialReaderException) ||
								(cause instanceof AnonymizerFilterException) ||
								(cause instanceof OdmSinkException))
						{
							message = cause.getMessage();
						} else {
							message = cause.toString();
						}
						
						StringWriter sw = new StringWriter();
						PrintWriter pw = new PrintWriter(sw);
						e.printStackTrace(pw);
						pw.flush();
						Logger.getRootLogger().error(sw.toString());
						pw.close();
						Logger.getRootLogger().info("Execution finished due to error.");

						showErrorMessage(message);
                	} finally {
                		Logger.getRootLogger().removeAllAppenders();
                		this.close();
                	}
	        	}
	        }
		}
	}
	
	public void registerWorker(TrialSubmitterSwingWorker tssw) {
		TrialSubmitterProgressMonitor tsMonitor = new TrialSubmitterProgressMonitor(
				tssw,
				jFrame,
				"Adding subjects to trial submission.",
				"", 0, 100);
		tsMonitor.setMillisToPopup(0);
		tsMonitor.setMillisToDecideToPopup(0);
	}
	
	public void showErrorMessage(String message) {
		Object feedback = message;
		
		HtmlJPanel messagePane = new HtmlJPanel(message);
		
		String osName = System.getProperty("os.name");
		
		if (osName.contains("Windows")) {
			int maxWidth = (int) jFrame.getPreferredSize().getWidth();
			FontMetrics fm = messagePane.getFontMetrics(messagePane.getFont());
			int numberOfLines = 10;
			int height = (numberOfLines) * fm.getHeight();

			messagePane.setPreferredSize(new Dimension(maxWidth, height));
			messagePane.scrollToTop();
			messagePane.scrollToLeft();
			feedback = messagePane;
		}
		
		JOptionPane.showMessageDialog(jFrame,
				feedback,
				"Error",
				JOptionPane.ERROR_MESSAGE);
	}

}
